﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Program.cs" company="YellowFish">
//   Copyright © YellowFish 2009
// </copyright>
// <summary>
//   Defines the Program type.
// </summary>
// <author>
// Roman "4ux-nbIx" Novitsky
// </author>
// --------------------------------------------------------------------------------------------------------------------

namespace OneNoteDataTypesCodeGenerator
{
  #region Imported Namespaces


  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Reflection;

  using YF.OneNote;


  #endregion


  internal class Program
  {
    #region Constants and Fields


    private static readonly string outputPath = "Result\\";
    private static string _collectionEventHandlerTemplate;
    private static string _collectionPropertyComparisionTemplate;
    private static string _contentClassTemplate;
    private static string _hierarchyClassTemplate;
    private static string _notesNodeClassTemplate;
    private static string _propertyComparisionTemplate;


    #endregion


    #region Methods


    private static void GenerateCode(Type type, string template)
    {
      if (!Directory.Exists(outputPath))
      {
        Directory.CreateDirectory(outputPath);
      }

      Stream stream = File.Open(outputPath + type.Name + "Generated.cs", FileMode.Create);

      using (var writer = new StreamWriter(stream))
      {
        PropertyInfo[] properties = type.GetProperties();

        string code = string.Empty;
        var collections = new List<CollectionInfo>();

        foreach (PropertyInfo propertyInfo in properties)
        {
          FieldInfo field = GetField(propertyInfo.Name, type);

          if (field != null)
          {
            Type[] genericArguments = field.FieldType.GetGenericArguments();

            if (genericArguments.Length == 1 && genericArguments[0].IsSubclassOf(typeof(NotesNode)))
            {
              var info = new CollectionInfo(genericArguments[0], field.Name);
              collections.Add(info);
            }

            string propertyCode = GeneratePropertyCode(propertyInfo.Name, field);
            code += propertyCode;
          }
        }

        string constructorCode = GenerateConstructor(collections);
        string collectionHandlersCode = GenerateCollectionHandlers(collections);

        string classCode = template;
        classCode = classCode.Replace("#constructor", constructorCode);
        classCode = classCode.Replace("#eventHandlers", collectionHandlersCode);
        classCode = classCode.Replace("#className", type.Name);
        classCode = classCode.Replace("#code", code);

        writer.Write(classCode);
      }
    }


    private static string GenerateCollectionHandlers(IEnumerable<CollectionInfo> collections)
    {
      string code = string.Empty;

      foreach (CollectionInfo info in collections)
      {
        string handlerCode = _collectionEventHandlerTemplate;
        handlerCode = handlerCode.Replace("#collectionName", info.CollectionName);
        handlerCode = handlerCode.Replace("#itemType", info.ItemType.Name);

        code += handlerCode;
      }

      return code;
    }


    private static string GenerateConstructor(IEnumerable<CollectionInfo> collections)
    {
      string eventsCode = string.Empty;

      foreach (CollectionInfo info in collections)
      {
        eventsCode += "{0}.CollectionChanged += {0}_CollectionChanged;".Replace("{0}", info.CollectionName);
      }

      return eventsCode;
    }


    private static string GeneratePropertyCode(string propertyName, FieldInfo field)
    {
      if (field == null || field.FieldType.IsArray)
      {
        return string.Empty;
      }

      string code;

      if (typeof(ICollection).IsAssignableFrom(field.FieldType))
      {
        code = _collectionPropertyComparisionTemplate;
      }
      else
      {
        code = _propertyComparisionTemplate;
      }

      code = code.Replace("#propertyType", field.FieldType.FullName);
      code = code.Replace("#property", propertyName);
      code = code.Replace("#field", field.Name);

      return code;
    }


    private static FieldInfo GetField(string propertyName, Type type)
    {
      FieldInfo field;
      string formattedFieldName = GetFormattedFieldName(propertyName);
      string generatedFieldName = GetGeneratedFieldName(propertyName);

      field = type.GetField(formattedFieldName, 
                            BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
      if (field == null)
      {
        field = type.GetField(generatedFieldName, 
                              BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
      }

      return field;
    }


    private static string GetFormattedFieldName(string propertyName)
    {
      string lower = propertyName[0].ToString().ToLower();
      propertyName = lower + propertyName.Substring(1);

      return "_" + propertyName;
    }


    private static string GetGeneratedFieldName(string propertyName)
    {
      return propertyName + "Field";
    }


    private static void Main(string[] args)
    {
      ReadTemplates();

      Assembly assembly = Assembly.Load("YF.OneNote");
      Type hierarchNodetype = assembly.GetType("YF.OneNote.NotesHierarchyNode");

      Type[] types = assembly.GetTypes();
      foreach (Type type in types)
      {
        if (!type.IsClass)
        {
          continue;
        }

        if (type.Namespace.Equals("YF.OneNote.DataTypes"))
        {
          if (type.IsSubclassOf(hierarchNodetype))
          {
            GenerateCode(type, _hierarchyClassTemplate);
          }
          else if (type.IsSubclassOf(typeof(NotesNode)))
          {
            GenerateCode(type, _notesNodeClassTemplate);
          }
          else
          {
            GenerateCode(type, _contentClassTemplate);
          }
        }
      }
    }


    private static void ReadTemplates()
    {
      using (var reader = new StreamReader("hierarchyClassTemplate.txt"))
      {
        _hierarchyClassTemplate = reader.ReadToEnd();
      }

      using (var reader = new StreamReader("contentClassTemplate.txt"))
      {
        _contentClassTemplate = reader.ReadToEnd();
      }

      using (var reader = new StreamReader("notesNodeClassTemplate.txt"))
      {
        _notesNodeClassTemplate = reader.ReadToEnd();
      }

      using (var reader = new StreamReader("propertyComparisionTemplate.txt"))
      {
        _propertyComparisionTemplate = reader.ReadToEnd();
      }

      using (var reader = new StreamReader("collectionPropertyComparisionTemplate.txt"))
      {
        _collectionPropertyComparisionTemplate = reader.ReadToEnd();
      }

      using (var reader = new StreamReader("collectionEventHandlerTemplate.txt"))
      {
        _collectionEventHandlerTemplate = reader.ReadToEnd();
      }
    }


    #endregion
  }
}